﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using YetAnotherChat.TcpServer.WebSocketProtocols;
using YetAnotherChat.TcpServer.WebSocketProtocols.rfc6455;

namespace YetAnotherChat.TcpServer
{
    /// <summary>
    /// ClientConnection wraps around TcpClient that the client use to send/receive msg to a server.
    /// </summary>
    public class ClientConnection: Connection
    {
        private WebSocketClient _webSocketClient;

        public ClientConnection(WebSocketClient webSocketClient, TcpClient connection, WebSocketProtocolFactory webSocketProtocolFactory)
            : base(connection, webSocketProtocolFactory)
        {
            _webSocketClient = webSocketClient;
        }

        public bool IsAuthenticated { get; set; }

        protected override void ProcessReceivedMessage()
        {
            if (!IsAuthenticated)
            {
                var someBytes = new byte[this.ReceivedByteCount];
                Array.Copy(Buffer, 0, someBytes, 0, ReceivedByteCount);
                _protocol.DataFrame.Append(someBytes);
                var authenticated = CheckAuthenticationResponse();
                _protocol.DataFrame.Reset();

                if (!authenticated)
                {
                    Disconnect();
                }
            }
            else
            {
                _protocol.DataFrame.Append(Buffer, true);
                if (_protocol.DataFrame.State == DataFrameBase.DataState.Complete)
                {
                    _webSocketClient.OnMessageReceived(new MessageEventArgs(this));
                    _protocol.DataFrame.Reset();
                }
            }
        }

        private ClientHandshake _handshake;
        public string[] SubProtocols;
        public string CurrentProtocol { get; private set; }

        public override void SetupSendReceiveProcess()
        {
            base.SetupSendReceiveProcess();
            if (!IsAuthenticated)
            {
                Authenticate();
            }
        }

        private void Authenticate()
        {
            _handshake = new ClientHandshake { Version = "8", Origin = _webSocketClient.Origin, Host = _webSocketClient.Host, Key = GenerateKey(), ResourcePath = _webSocketClient.Path, SubProtocols = SubProtocols };

            Send(Encoding.UTF8.GetBytes(_handshake.ToString()));
        }

        private static String GenerateKey()
        {
            var bytes = new byte[16];
            var random = new Random();

            for (var index = 0; index < bytes.Length; index++)
            {
                bytes[index] = (byte)random.Next(0, 255);
            }

            return Convert.ToBase64String(bytes);
        }

        private bool CheckAuthenticationResponse()
        {
            var receivedData = _protocol.DataFrame.ToString();
            var header = new Header(receivedData);
            var handshake = new ServerHandshake(header);

            if (Authentication.GenerateAccept(_handshake.Key) != handshake.Accept) return false;

            if (SubProtocols != null)
            {
                if (header.SubProtocols == null)
                {
                    return false;
                }

                foreach (var s in SubProtocols)
                {
                    if (header.SubProtocols.Contains(s) && String.IsNullOrEmpty(CurrentProtocol))
                    {
                        CurrentProtocol = s;
                    }

                }
                if (String.IsNullOrEmpty(CurrentProtocol))
                {
                    return false;
                }
            }

            //ReadyState = ReadyStates.OPEN;
            IsAuthenticated = true;
            Connected = true;
            //_connecting = false;
            //context.UserContext.OnConnected();
            return true;
        }
    }
}
